
ENT.Type = "anim"
ENT.Base = "base_gmodentity"

ENT.PrintName = "Server"
ENT.Author = "[GU]|ZLC|thomasfn"
ENT.Contact = "Don't."
ENT.Instructions = "Spawn it, then press e to use it."
ENT.Purpose = ""

ENT.Spawnable = false
ENT.AdminSpawnable = false

ENT.ItemModel = "models/props_lab/harddrive02.mdl"
util.PrecacheModel( ENT.ItemModel )

ENT.IsServer = true
ENT.ServerType = ""

if (SERVER) then

	AddCSLuaFile( "shared.lua" )
	
	ENT.NextUse = CurTime()

	function ENT:Initialize()
		self.Entity:SetModel(self.ItemModel)
		self.Entity:PhysicsInit(SOLID_VPHYSICS)
		self.Entity:SetMoveType(MOVETYPE_VPHYSICS)
		self.Entity:SetSolid(SOLID_VPHYSICS)
		
		if (!self.Data) then self.Data = {} end
		if (!self.Data[self:EntIndex()]) then self.Data[self:EntIndex()] = {} end

		self:UpdateBool( "IsOn", false )
		self.Data[self:EntIndex()].Router = nil
		self:UpdateStr( "IP", "192.168.0.1" )
		self.Data[self:EntIndex()].Online = false
		self.Data[self:EntIndex()].PortList = {}
		
		// Wire Inputs
		self.Data[self:EntIndex()].WireInputs = {}
		
		self:OpenPort( 50, "ent" ); -- Ping Port
		self:OpenPort( 73, "ent" ); -- Global List Port
		
		if (!self.Password) then self.Password = "" end
		
		self:SetServerType( "chat" )
		
		local phys = self.Entity:GetPhysicsObject()
		if (phys && phys:IsValid()) then phys:Wake() end
	end
	
	function ENT:SetServerType( stype )
		self.ServerType = stype
	
		self:SetNWString( "Type", self.ServerType )
		
		if (self.ServerType == "chat") then
			self:OpenPort( 18, "ent" ); -- GChat Host Port
			
			if (!self.Data[self:EntIndex()].ConPCs) then self.Data[self:EntIndex()].ConPCs = {} end
		end
		if (self.ServerType == "wire") then
			if (WireAddon==1) then
				self:OpenPort( 25, "ent" ); -- Wire Host Port
				
				self.WireDebugName = self.PrintName
				self.Inputs = Wire_CreateInputs(self.Entity, { "1", "2", "3", "4", "5" })
				self.Outputs = Wire_CreateOutputs(self.Entity, {"1", "2", "3", "4", "5" })
			end
		end	
	end
	
	function ENT:Use( activator, ply )
		if (CurTime() < self.NextUse) then return end
		self.NextUse = CurTime()+1
		local IsOn = self.Data[self:EntIndex()].IsOn
		if (!IsOn) then
			self:UpdateBool( "IsOn", true )
			ply:PrintMessage( HUD_PRINTTALK, "Server is now on." )
			
			self:TurnedOn()
			
			return
		end
		self:UpdateBool( "IsOn", false )
		ply:PrintMessage( HUD_PRINTTALK, "Server is now off." )

		self:TurnedOff()
	end
	
	function ENT:TurnedOn()
		if (self.ServerType == "chat") then
			//self:OpenPort( 18, "ent" )
		end
		if (self.ServerType == "wire") then
			//self:OpenPort( 25, "ent" )
		end
		
		// Register us with router
		local router = self.Data[self:EntIndex()].Router
		if ((router) && (router:IsValid())) then
			ip = router:RegisterPC( self )
			if (ip) then
				self:SetIP( ip )
				self:UpdateBool( "Online", true )
			end
		else
			self.Data[self:EntIndex()].Router = nil
		end
	end
	
	function ENT:TurnedOff()
		self.Data[self:EntIndex()].ConPCs = {}
		//self:ClosePort( 18 )
		//self:ClosePort( 25 )
		self:UpdateStr( "IP", "192.168.0.1" )
		
		// Unregister us
		local router = self.Data[self:EntIndex()].Router
		if ((router) && (router:IsValid())) then
			router:UnregisterPC( self )
			self:SetIP( "192.168.0.1" )
			self:UpdateBool( "Online", false )
		else
			self.Data[self:EntIndex()].Router = nil
		end
	end
	
	function ENT:TriggerInput(iname, value)
		if (self.ServerType == "wire") then
			self.Data[self:EntIndex()].WireInputs[ iname ] = value
		end
	end
	
	function ENT:DoPacket( Packet )
		// This is called when a packet has been aimed at a port opened by the OS
		if (!Packet) then return end		

		// ALL SERVERS
		local port = Packet[3]
		local ip = Packet[1]
		if (Packet[4][1] == "ping") and (port == 50) then
			// We have been pinged!
		end
		
		//Stop here if the password is wrong.
		local pass = ""
		if (Packet[5]) then pass = Packet[5] end
		
		// if (self.Password != "") and (pass != self.Password) then
			// NOTE: Removed server passwords for the time being.
			// TODO: Re-implement this, in a working state.
		// end
		
		
		// WIRE SERVER
		if ((self.ServerType == "wire") && (port == 25) && (WireAddon == 1)) then
			if (Packet[4][1] == "svr_wire_output") then
				Wire_TriggerOutput(self.Entity, Packet[4][2], tonumber( Packet[4][3] ))
			end
			if (Packet[4][1] == "svr_wire_getinputs") then
				local data = {}
				data = self.Data[self:EntIndex()].WireInputs
				local packet = { self.Data[self:EntIndex()].IP, ip, 24, { "wireinputs", data } }
				local result = self:SendPacket( packet )
			end
		end
		
		
		// CHAT SERVER
		if (self.ServerType == "chat") and (port == 18) then
			if (Packet[4][1] == "svr_gchat_register") then
				obj = self.Data[self:EntIndex()].ConPCs
				for i=1,#obj do
					if (obj[i] == ip) then return end
				end
				table.insert( self.Data[self:EntIndex()].ConPCs, ip )
				
				local p = {self.Data[self:EntIndex()].IP, ip, 17, { "chat", "[Connection Successful]" } }
				local result = self:SendPacket( p )
				
				for i=1,#obj do
					local p = { self.Data[self:EntIndex()].IP, obj[i], 17, { "chat", tostring(ip) .. " has connected." } }
					local result = self:SendPacket( p )
				end
			end
			if (Packet[4][1] == "svr_gchat_unregister") then
				obj = self.Data[self:EntIndex()].ConPCs
				for i=1,#obj do
					if (obj[i] == ip) then table.remove( self.Data[self:EntIndex()].ConPCs, i ); return; end
				end
				for i=1,#obj do
					local p = { self.Data[self:EntIndex()].IP, obj[i], 17, { "chat", tostring(ip) .. " has disconnected." } }
					local result = self:SendPacket( p )
				end
			end
			if (Packet[4][1] == "svr_gchat_message") then
				obj = self.Data[self:EntIndex()].ConPCs
				for i=1,#obj do
					local p = { self.Data[self:EntIndex()].IP, obj[i], 17, { "chat", tostring(ip) .. ": " .. Packet[4][2] } }
					local result = self:SendPacket( p )
				end
			end
		end
	end
	
	function ENT:TriggerInput(iname, value)
		if ((self.ServerType == "wire") && (WireAddon == 1)) then
			self.Data[self:EntIndex()].WireInputs[ iname ] = value
		end
	end
	
	function ENT:OpenPort( port, command )
		if (self:PortOpen( port )) then return end
		table.insert( self.Data[self:EntIndex()].PortList, {port,command} )
	end
	
	function ENT:ClosePort( port )
		if (!self:PortOpen( port )) then return end
		ports = self.Data[self:EntIndex()].PortList
		for i=1,#ports do
			if (ports[i][1] == port) then
				table.remove( self.Data[self:EntIndex()].PortList, i )
			end
		end
	end
	
	function ENT:PortOpen( port )
		ports = self.Data[self:EntIndex()].PortList
		for i=1,#ports do
			if (ports[i][1] == port) then return true end
		end
	end
	
	function ENT:GetPortCommand( port )
		ports = self.Data[self:EntIndex()].PortList
		for i=1,#ports do
			if (ports[i][1] == port) then return ports[i][2] end
		end
		return "none"
	end
	
	// DeliveryCode:
	// 0 = Cannot Deliver
	// 1 = Delivered
	// 2 = Destination not found
	// 3 = No Network
	// 4 = Packet Denied: Closed Port
	// 5 = Packet Denied: Blocked IP
	// 6 = Packet Denied: Invalid packet

	function ENT:SendPacket( Packet )
		if (!Packet) then return 6 end
		dest = Packet[2]
		if (!dest) then return 6 end
		if (dest == "127.0.0.1") then
			return self:PacketDelivered( Packet )
		end
		if (!self.Data[self:EntIndex()].Router) or (!self.Data[self:EntIndex()].Online) then return 3 end
		return self.Data[self:EntIndex()].Router:DeliverPacket( Packet )
	end
	
	function ENT:PacketDelivered( Packet )
		if (!self.Data[self:EntIndex()].IsOn) then return 0 end
		
		// First we deal with the packet header
		if (!Packet) then return 6 end
		source = Packet[1]
		dest = Packet[2]
		port = Packet[3]
		body = Packet[4]
		myip = self.Data[self:EntIndex()].IP
		if (!self:PortOpen(port)) then return 4 end
		if (dest != myip) and (!((dest == "127.0.0.1") and (source == myip))) then return 0 end
		if (self:IPBlocked(source)) then return 5 end
		// Now we deal with the packet body
		com = self:GetPortCommand( port )
		if (com == "none") then
			return 1
		end
		if (com == "ent") then
			self:DoPacket( Packet )
			return 1
		end
		return 6
	end
	
	function ENT:IPBlocked( IP )
		return false
	end
	
	function ENT:Think()
		if (!self.Data[self:EntIndex()].IsOn) then return end
		
		// If we do not have a router
		if (!self.Data[self:EntIndex()].Router) then
			// Make us offline
			self:UpdateBool( "Online", false )
			self:SetIP( "192.168.0.1" )
		end
		
		// If we DO have a router AND we are offline
		if (self.Data[self:EntIndex()].Router) && (!self.Data[self:EntIndex()].Online) then
			// Attempt a connection
			ip = self.Data[self:EntIndex()].Router:RegisterPC( self )
			if (ip) then
				self:SetIP( ip )
				self:UpdateBool( "Online", true )
			end
		end
	end
	
	function ENT:UpdateInt( var, value )
		self.Data[self:EntIndex()][var] = value
		self:SetNWInt( var, value )
	end
	
	function ENT:UpdateBool( var, value )
		self.Data[self:EntIndex()][var] = value
		self:SetNWBool( var, value )
	end
	
	function ENT:UpdateStr( var, value )
		self.Data[self:EntIndex()][var] = value
		self:SetNWString( var, value )
	end
	
	function ENT:SetIP( IP )
		self.Data[self:EntIndex()].IP = IP
		self:UpdateStr( "IP", IP )
	end
	
end

if (CLIENT) then

	function ENT:Draw()
		// Draw our entity
		self.BaseClass.Draw( self )
		
		// Draw the wire stuff
		if (self.ServerType == "wire") then
			Wire_Render(self.Entity)
		end
	end
	
end